package gbench.sandbox.jdbc;

import static gbench.common.tree.LittleTree.*;
import static gbench.common.tree.LittleTree.IRecord.*;
import static gbench.common.tree.LittleTree.Tuple2.*;
import static gbench.common.tree.LittleTree.SQL.*;

import java.time.LocalDateTime;
import java.util.DoubleSummaryStatistics;
import java.util.LinkedList;
import java.util.Random;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;

import gbench.common.tree.LittleTree.IRecord;
import gbench.common.tree.LittleTree.SQL;
import gbench.common.tree.LittleTree.Tuple2;
import gbench.common.tree.LittleTree.Jdbc.IJdbcSession;
import gbench.commonApp.data.DataApp;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

public class JunitDB2 {
    
    // 数据操作类
    final class MyDataApp extends DataApp {
        public MyDataApp(String dbname) { this.reload(this.configureDbName(dbname));}
        public IRecord configureDbName(final String dbname) { return 
            cfg.derive("url", MFT("jdbc:mysql://localhost:3306/{0}?serverTimezone=GMT%2B8", dbname));
        }
    }; // MyDataApp 数据数据库操作类
    
    /**
     * 求和
     * @param stream
     * @param mapper
     * @return
     */
    public DoubleSummaryStatistics dblstats(Stream<?> stream, Function<IRecord, Double> mapper) {
        return stream.map(e -> (IRecord) e).collect(Collectors.summarizingDouble(e -> mapper.apply(e)));
    }
    
    /**
     * 
     * @author gbench
     *
     */
    @Data @AllArgsConstructor @NoArgsConstructor
    public static class StatsItem{
        Double avg;
        Double count;
        Double sum;
        public String toString() {
            return MFT("avg:{0},count:{1},total:{2,number,#}",avg,count,sum) ;
        }
    }
    
    @SuppressWarnings("unused")
    @Test
    public void foo() {
        // create database db2
        final var TBL_NUM = 100; // 数据表个数
        final var ROW_NUM = 100000; // 最大数据行数
        final var proto = REC("#id",1,"name","甲苯","amount",2d,"createtime",LocalDateTime.now());// 原型结构
        final var tables = LIST( NATS(TBL_NUM).map(i -> MFT( "T_ORDER_{0}", i)) ); // 表名
        final var keys = proto.aoks2rec(compose_f(e->e.str("name"),SQL::parseFieldName)).filter2("id").keys(); // 提取键名列表; // 键名
        final var s1 = JOIN( keys, "," ); // 设置键名
        final var s2 = JOIN( keys.stream().map(e -> "e." + e), "," ); // 键名
        final var rand = new Random(); // 随机处理
        final var names = "甲苯,甲醇,乙醇,丁二醇".split(","); // 产品名称
        final var tbl2dbs = GROUP2MAP(NATS(TBL_NUM).map( i -> REC ( // 数据表配置
            "id", i, // id 编号
            "tblname", MFT("T_ORDER_{0}", i), // 表名
            "dbname", MFT("mydb{0}", i % 2 + 1) // 数据库名
        )), rec -> rec.str("tblname")); // 采用名称生成 数据表
        
        final Consumer<Tuple2<IJdbcSession<UUID, Object>,String>> data_prepare = tup -> { // 表数据插入
            final var sess = tup._1(); // 会话
            final var tblname = tup._2(); // 表名
            final var sb = SQL.builder(tblname); // 狗仔entry sql 系列构建器具
            final var rb = IRecord.builder(proto.filter2("#id")); // 去除id
            final var createsqls = sb.get(proto).createTable(true); // 生成createsqls
            final var last = timeit( () -> { // 计时统计
                TUPLES(createsqls).filter(t->t._1()!=1).map(t->t._2()).forEach(trycatch2(sess::sqlexecute));
                Stream.of( nsql ( "insert delayed into " + tblname + " (" + s1 + ") values ${ foreach e in entries (" + s2 + ") }", 
                    REC( "entries", LIST( NATS(ROW_NUM).map( i -> A ( rnd(names), rand.nextInt(1000), LocalDateTime.now() ) ).map(rb::get) ) ) 
                ) ).forEach( trycatch2(sess::sqlexecute) );
            });// timeit
            System.out.println( MFT("{0} last for {1} s\n", tblname, last/1000) ); // 显示数据执行时间
        }; // data_prepare
        
        final var prepare_last = timeit( () -> {
            tables.parallelStream().map( tbl -> TUP2( tbl, new MyDataApp ( tbl2dbs.get(tbl).str("dbname") ) ) )
            .forEach( tup -> tup._2().withTransaction( sess -> data_prepare.accept( TUP2(sess,tup._1()) ) ) );
        }); // withTransaction
        
        final var sts_last = timeit( () -> {
            final var recs = new LinkedList<Stream<IRecord>>();
            
            // 数据提取
            tables.stream().map( tbl -> TUP2( tbl, new MyDataApp ( tbl2dbs.get(tbl).str("dbname") ) ) )
            .forEach( tup -> tup._2().withTransaction( sess ->{
                final var sql = MFT("select ''{0}'' tblname, name, count(*) cnt, sum(amount) sum, avg(amount) avg from {0} group by name",tup._1());
                recs.add(sess.sql2stream(sql));
            })); // forEach
            
            // 合并统计
            final var rec = recs.parallelStream().flatMap( e -> e )
            .map(e->REC(
                MFT("{0}", e.get("name"), e.get("tblname")), // path
                REC("cnt", e.get("cnt"), "sum", e.get("sum"), "avg", e.get("avg") ) // value
            )).collect(llclc(IRecord::RECMERGE2))
            .dfs_eval_forall( (String path, Stream<IRecord> stream ) -> 
                REC(path.substring(1),dblstats(stream,e->e.dbl("cnt")).getSum())
            ).stream().collect(llclc(IRecord::RECMERGE2));
            
            System.out.println(rec);
        }); // withTransaction
        
    }

}
